home *** CD-ROM | disk | FTP | other *** search
/ Programming a Multiplayer FPS in DirectX / Programming a Multiplayer FPS in DirectX (Companion CD).iso / DirectX / dxsdk_oct2004.exe / dxsdk.exe / Samples / C++ / Direct3D / StateManager / renderables.h < prev    next >
Encoding:
C/C++ Source or Header  |  2004-09-27  |  19.7 KB  |  489 lines

  1. //--------------------------------------------------------------------------------------
  2. // File: renderables.h
  3. //
  4. // Material Management classes for EffectStateManager 'StateManager' Sample
  5. // These classes serve in support of the sample, but are not directly related to the
  6. // intended topic of the ID3DXEffectStateManager Interface.
  7. //
  8. // Copyright (c) Microsoft Corporation. All rights reserved.
  9. //--------------------------------------------------------------------------------------
  10.  
  11. #pragma once
  12.  
  13. //--------------------------------------------------------------------------------------
  14. // STL dependancies
  15. //--------------------------------------------------------------------------------------
  16. #pragma warning ( push )
  17. #pragma warning ( disable : 4512 ) // 'class' : assignment operator could not be generated
  18. #pragma warning ( disable : 4702 ) // unreachable code
  19. #include <map>
  20. #include <string>
  21. #include <vector>
  22. #include <utility>
  23. #include <algorithm>
  24. #include <exception>
  25. #pragma warning ( pop )
  26. using std::map;
  27. using std::vector;
  28. using std::pair;
  29. using std::string;
  30. using std::wstring;
  31. using std::remove;
  32. using std::sort;
  33. using std::find;
  34. using std::lexicographical_compare;
  35. using std::exception;
  36. using std::bad_alloc;
  37.  
  38.  
  39. //--------------------------------------------------------------------------------------
  40. // Allows STL Map containers to use case-insensitive string comparisons
  41. //--------------------------------------------------------------------------------------
  42. template <typename T>
  43. class compare_case_insensitive
  44. {
  45. protected:
  46.     static bool compare( typename T::value_type c1, typename T::value_type c2 )
  47.     {
  48.         return tolower(c1) < tolower(c2);
  49.     }
  50. public:
  51.     bool operator()( const T& s1, const T& s2 ) const
  52.     {
  53.         return lexicographical_compare( s1.begin(), s1.end(),
  54.                                         s2.begin(), s2.end(),
  55.                                         compare );
  56.     }
  57. };
  58.  
  59. typedef compare_case_insensitive<wstring> wcompare;
  60. typedef compare_case_insensitive<string> scompare;
  61.  
  62. //--------------------------------------------------------------------------------------
  63. // Provides tracking of DX device-dependant Resources, which may require attention
  64. // following device lost, device reset, etc events.
  65. // This templated-class also provides a means to 'singularize' resources.  Eg, if
  66. // multiple instances of the same resource are requested, only one instance of the
  67. // resource will be loaded.  This is accomplished via an STL map -- see implementation
  68. // comments for further details.
  69. //--------------------------------------------------------------------------------------
  70. template <typename _dxty>
  71. class CResource
  72. {
  73. public:
  74.     typedef map<wstring,CResource*, wcompare > resourceMap;
  75. protected:
  76.     _dxty m_pResource;                  // Pointer to the underlying device-dependant resource
  77.     ULONG m_ulRef;                      // A reference count of the number of requested instances
  78.     wstring m_wsName;                   // The filename of the resource
  79.     static resourceMap m_mapLoaded;     // An STL map used to track all loaded instances of this
  80.                                         // resource type
  81. public:
  82.     // Constructor is called when a new instance of a resource is loaded
  83.     CResource( const wstring tsName, const _dxty pResource )
  84.         : m_wsName( tsName ), m_pResource( pResource ), m_ulRef( 1UL )
  85.     {
  86.         // Add the loaded resource into the STL map
  87.         // The map allows us to detect duplicate requests for the same resource
  88.         // (textures, meshes, effects, etc).
  89.         // The map also allows tracking of resources that are required to take
  90.         // action following device lost, etc events
  91.         m_mapLoaded.insert( resourceMap::value_type( m_wsName, this ) );
  92.     }
  93.     
  94.     // Destructor is called when all instances of the resource have been released
  95.     virtual ~CResource()
  96.     {
  97.         // erase the instance from the map of loaded resources
  98.         resourceMap::iterator it = m_mapLoaded.find( m_wsName );
  99.         if( m_mapLoaded.end() != it )
  100.             m_mapLoaded.erase( it );
  101.         
  102.         // release (unload) the underlying resource
  103.         m_pResource->Release();
  104.     }
  105.     
  106.     // Entry point for resource to take action following Lost Device Event
  107.     virtual HRESULT OnLostDevice() = 0;
  108.     
  109.     // Entry point for resource to take action prior to Reset Device Event
  110.     virtual HRESULT OnResetDevice() = 0;
  111.     
  112.     // Must be called when the resource is re-instanced (eg, if you make a new copy
  113.     // of the resource pointer and keep it active, you must call AddRef)
  114.     ULONG AddRef()
  115.     {
  116.         return ++m_ulRef;
  117.     }
  118.     
  119.     // Called to de-instance the resource (eg, retire a pointer that was previously
  120.     // obtained, or AddRef'd)
  121.     ULONG Release()
  122.     {
  123.         ULONG ulRef = --m_ulRef;
  124.         
  125.         // when no further instances are outstanding, delete the resource
  126.         if( 0UL == m_ulRef )
  127.             delete this;
  128.         
  129.         return ulRef;
  130.     }
  131.     
  132.     // Static Entry-Point to the resource list, for LostDevice event
  133.     // Each active resource of this type will be iterated, and it's OnDeviceLost
  134.     // entry point will be called.
  135.     static HRESULT LostDevice()
  136.     {
  137.         bool bResult = true;
  138.         
  139.         // Iterate through the list of loaded resources
  140.         for( resourceMap::iterator it = m_mapLoaded.begin();
  141.             it != m_mapLoaded.end();
  142.             it++ )
  143.             // Notify the instance of this resource that the device is lost
  144.             // by calling it's OnLostDevice entry point
  145.             bResult &= SUCCEEDED( (*it).second->OnLostDevice() );
  146.         
  147.         return bResult ? S_OK : E_FAIL;
  148.     }
  149.     
  150.     // Static Entry-Point to the resource list, for ResetDevice event
  151.     // Each active resource of this type will be iterated, and it's OnResetDevice
  152.     // entry point will be called.
  153.     static HRESULT ResetDevice()
  154.     {
  155.         bool bResult = true;
  156.  
  157.         // Iterate through the list of loaded resources
  158.         for( resourceMap::iterator it = m_mapLoaded.begin();
  159.             it != m_mapLoaded.end();
  160.             it++ )
  161.             // Notify the instance of this resource that the device is lost
  162.             // by calling it's OnLostDevice entry point
  163.             bResult &= SUCCEEDED( (*it).second->OnResetDevice() );
  164.  
  165.         return bResult ? S_OK : E_FAIL;
  166.     }
  167.     
  168.     // Allows the resource list to determine whether or not a resource
  169.     // by this (file)name already exists
  170.     static CResource* getExisting( wstring tsName )
  171.     {
  172.         // Locate any existing resource of this name
  173.         resourceMap::iterator it = m_mapLoaded.find( tsName );
  174.         
  175.         // No resource was located, return NULL
  176.         if( m_mapLoaded.end() == it )
  177.             return NULL;
  178.  
  179.         // A matching resource was found, increment it's ref count and return the
  180.         // pointer to the resource
  181.         (*it).second->AddRef();
  182.         return (*it).second;
  183.     }
  184.  
  185.     const wstring& getName() const { return m_wsName; }
  186.  
  187.     // Obtain the current pointer to the underlying resource
  188.     inline _dxty getPointer() { return m_pResource; }
  189. };
  190.  
  191.  
  192. //--------------------------------------------------------------------------------------
  193. // Texture Resource Management
  194. //--------------------------------------------------------------------------------------
  195. class CTexture : public CResource<LPDIRECT3DBASETEXTURE9>
  196. {
  197. protected:
  198.     LPDIRECT3DTEXTURE9 m_pNormalMap;            // A normal map equivalent
  199.                                                 // If an effect specifies that the texture should
  200.                                                 // be converted, the original texture owns the converted child
  201.                                                 // This avoids maintaining multiple converted normal maps
  202.                                                 // on a per-effect basis.
  203. public:
  204.     // Called following the loading of a new texture
  205.     inline CTexture( const wstring tsName, const LPDIRECT3DBASETEXTURE9 pTexture )
  206.         : CResource<LPDIRECT3DBASETEXTURE9>( tsName, pTexture ), m_pNormalMap( NULL )
  207.     {
  208.     }
  209.     ~CTexture()
  210.     {
  211.         SAFE_RELEASE( m_pNormalMap );
  212.     }
  213.     
  214.     // Entry point to lost device handling
  215.     virtual HRESULT OnLostDevice();
  216.     
  217.     // Entry point to device reset handling
  218.     virtual HRESULT OnResetDevice();
  219.     
  220.     // Retrieves a normal map for this texture
  221.     HRESULT GetNormalMapPtr(LPDIRECT3DBASETEXTURE9* ppTexture);
  222.     
  223.     // Called to create a new texture resource.  If the texture has been previously
  224.     // loaded, the existing instance of the texture will be returned.
  225.     static HRESULT Create( LPDIRECT3DDEVICE9 pDevice, LPCWSTR wszFilename, CTexture** ppTexture );
  226. };
  227.  
  228.  
  229. //--------------------------------------------------------------------------------------
  230. // D3DX Effects Resource Management
  231. //--------------------------------------------------------------------------------------
  232. class CEffect : public CResource<LPD3DXEFFECT>
  233. {
  234. protected:
  235.     // Cached Handles to common effect parameters
  236.     D3DXHANDLE m_hMatViewProj;
  237.     D3DXHANDLE m_hMatProjection;
  238.     D3DXHANDLE m_hMatWorld;
  239.     D3DXHANDLE m_hMatView;
  240.     D3DXHANDLE m_hMatViewInv;
  241.     UINT       m_nFrameTimestamp;    // Used to avoid redundantly setting view & projection matrices
  242. public:
  243.     // Called following the loading of a new effect
  244.     CEffect( const wstring tsName, const LPD3DXEFFECT pEffect );
  245.  
  246.     // Entry point to lost device handling
  247.     virtual HRESULT OnLostDevice();
  248.  
  249.     // Entry point to device reset handling
  250.     virtual HRESULT OnResetDevice();
  251.     
  252.     // Setup the transformation matrices within the effect
  253.     virtual HRESULT SetMatrices( const D3DXMATRIX* pWorld,
  254.                                  const D3DXMATRIX* pView,
  255.                                  const D3DXMATRIX* pProjection,
  256.                                  UINT  nFrameTimeStamp );
  257.  
  258.     // Called to change the state manager interface for all loaded effects
  259.     static HRESULT SetStateManager( LPD3DXEFFECTSTATEMANAGER pStateManager );
  260.  
  261.     // Called to create a new d3dx effect resource.  If the effect has been
  262.     // previously loaded, the existing instance of the effect will be returned.
  263.     static HRESULT Create( LPDIRECT3DDEVICE9 pDevice, LPCWSTR wszFilename, CEffect** ppEffect );
  264. };
  265.  
  266.  
  267. //--------------------------------------------------------------------------------------
  268. // Used to bind a mesh material EffectInstance to a D3DX Effect
  269. // A list of all parameter default values is mantained, along with a cached handle to
  270. // the parameter within the effect.
  271. // Groups of effect parameters (which represent material settings) are sent into the
  272. // D3DX Effect via the apply() method.
  273. //--------------------------------------------------------------------------------------
  274. class CEffectInstance
  275. {
  276. public:
  277.     // parameter names are maintained in ANSI-format.  This is because D3DXHANDLE values are
  278.     // obtained from GetParameterByName -- which has ANSI-only string inputs
  279.     typedef vector<BYTE>                   instance_value;       // Holds the Value of one parameter
  280.     typedef map<D3DXHANDLE,instance_value> instance_params;      // Maps Parameter Handles to Values
  281.     typedef pair<string,wstring>           texture_annotation;   // Holds a matching texture annotation name/filename
  282.     typedef map<string,wstring>            texture_annotations;  // Maps texture annotation names to filenames
  283. protected:
  284.     LPD3DXEFFECT m_pEffect;                         // A pointer to the effect (for which D3DXHANDLE values are cached)
  285.     instance_params     m_instanceParams;           // List of Effect Default Values
  286.     texture_annotations m_textureAnnotations;       // List of Texture Filenames
  287.  
  288.     // set an effect instance parameter value (this value will be loaded into the effet
  289.     // when apply() is called)
  290.     void setValue( string name, BYTE* pValue, UINT nSizeInBytes );
  291.  
  292. public:
  293.     //
  294.     CEffectInstance( const D3DXEFFECTINSTANCE* pEffectInstance, LPD3DXEFFECT pEffect );
  295.     
  296.     //
  297.     ~CEffectInstance();
  298.     
  299.     // Called to bind a direct3d texture to the effect instance
  300.     HRESULT addTexture( string annotation_name, CTexture* pTexture );
  301.     
  302.     // Called to apply all Effect Instance Values to the D3DX Effect
  303.     HRESULT apply() const;
  304.     
  305.     // Returns a list of texture annotation values obtained from the effect instance.
  306.     // The client uses this to load all required textures, calling addTexture() for each one.
  307.     inline const texture_annotations& getTextureAnnotations( ) { return m_textureAnnotations; };
  308. };
  309.  
  310.  
  311. //--------------------------------------------------------------------------------------
  312. // Used to bind an effect instance and texture list to an effect file.  Together, these
  313. // uniquely specify a material.
  314. //--------------------------------------------------------------------------------------
  315. class CMaterial
  316. {
  317. protected:
  318.     CEffect*          m_pEffect;            // A pointer to the effect this material uses
  319.     CEffectInstance*  m_pEffectInstance;    // A pointer to the effect instance values
  320.     vector<CTexture*> m_vecTextures;        // A list of textures for this material
  321. public:
  322.     //
  323.     CMaterial( LPDIRECT3DDEVICE9 pDevice, D3DXEFFECTINSTANCE* pEffectInstance );
  324.     
  325.     //
  326.     ~CMaterial();
  327.     
  328.     //
  329.     inline CEffect* getEffect()                 { return m_pEffect; }
  330.     inline CEffectInstance* getEffectInstance() { return m_pEffectInstance; }
  331. };
  332.  
  333.  
  334. //--------------------------------------------------------------------------------------
  335. // D3DX Mesh Resource Management
  336. //--------------------------------------------------------------------------------------
  337. class CMeshObject : public CResource<LPD3DXMESH>
  338. {
  339. protected:
  340.     vector<CMaterial*> m_vecMaterialList;       // list of materials used within the mesh
  341. public:
  342.     //
  343.     CMeshObject( LPDIRECT3DDEVICE9 pDevice, wstring tsName, const LPD3DXMESH pMesh,
  344.                  D3DXEFFECTINSTANCE* pEffectInstance, DWORD dwMaterials );
  345.     
  346.     //
  347.     virtual ~CMeshObject();
  348.     
  349.     // Entry point to lost device handling
  350.     virtual HRESULT OnLostDevice();
  351.  
  352.     // Entry point to device reset handling
  353.     virtual HRESULT OnResetDevice();
  354.  
  355.     // Called to create a new D3DX Mesh resource.  If the mesh has been
  356.     // previously loaded, the existing instance of the mesh will be returned.
  357.     static HRESULT Create( LPDIRECT3DDEVICE9 pDevice, LPCWSTR wszFilename, CMeshObject** ppMesh );
  358.  
  359.     // Called to retrieve the number of mesh subsets
  360.     inline DWORD getSubsetCount()
  361.     {
  362.         return (DWORD)m_vecMaterialList.size();
  363.     }
  364.     
  365.     // Retrieves the material used by the given subset
  366.     inline CMaterial* getMaterial(DWORD dwSubset)
  367.     {
  368.         assert( dwSubset < m_vecMaterialList.size() );
  369.         return m_vecMaterialList[dwSubset];
  370.     }
  371. };
  372.  
  373.  
  374. //--------------------------------------------------------------------------------------
  375. // A CInstance object is used to specify an instance of an object to be drawn.
  376. // Multiple instances may use the same underlying object (m_pMesh) while each may have
  377. // its own unique world-space position (m_matWorld).
  378. //--------------------------------------------------------------------------------------
  379. class CInstance
  380. {
  381. protected:
  382.     D3DXMATRIXA16 m_matWorld;        // World Matrix for the instance of this object
  383.     CMeshObject*  m_pMesh;           // Mesh representation of this object
  384.     DWORD         m_dwRenderPass;    // Default render pass in which the object should be rendered
  385. public:
  386.     //
  387.     inline CInstance( CMeshObject* pMesh, D3DXMATRIX* pWorld, DWORD dwDefaultPass )
  388.         : m_pMesh( pMesh ), m_matWorld( *pWorld ), m_dwRenderPass( dwDefaultPass )
  389.     { }
  390.  
  391.     //
  392.     inline ~CInstance()
  393.     {
  394.         SAFE_RELEASE( m_pMesh );
  395.     }
  396.  
  397.     //
  398.     inline CMeshObject* getMeshObject() { return m_pMesh; }
  399.     inline D3DXMATRIX*  getMatrix()      { return &m_matWorld; }
  400.     inline DWORD        getRenderPass()         { return m_dwRenderPass; }
  401. };
  402.  
  403.  
  404. //--------------------------------------------------------------------------------------
  405. // A renderable object is the smallest unit that can be drawn by the sample.  It
  406. // corresponds to a binding between a Mesh Subset (plus it's associanted material)
  407. // and an instance of the mesh that provides the world position (m_pInstance).
  408. // For the purposes of this sample, sorting the render queue does is not required to
  409. // be particularly fast, as sorting occurs as a pre-render process.
  410. //--------------------------------------------------------------------------------------
  411. class CRenderable
  412. {
  413. protected:
  414.     // Defining values that uniquely specify the renderable item
  415.     CInstance*  m_pInstance;
  416.     DWORD       m_dwSubset;
  417.  
  418.     // Derived/cached values
  419.     // For the purposes of this sample, the material pointer may safely be cached.
  420.     // (Any action that invalidates the material pointer also results in rebuilding the
  421.     // list of renderables)
  422.     CMaterial* m_pMaterial;
  423.  
  424. public:
  425.     //
  426.     inline CRenderable( CInstance* pInstance, DWORD dwSubset )
  427.         : m_pInstance( pInstance ), m_dwSubset( dwSubset )
  428.     {
  429.         m_pMaterial = m_pInstance->getMeshObject()->getMaterial( m_dwSubset );
  430.     }
  431.     
  432.     //
  433.     inline CInstance* getInstance()             { return m_pInstance; }
  434.     inline DWORD      getSubset()               { return m_dwSubset; }
  435.     inline CEffect*   getEffect()               { return m_pMaterial->getEffect(); }
  436.     inline CEffectInstance* getEffectInstance() { return m_pMaterial->getEffectInstance(); }
  437. };
  438.  
  439.  
  440.  
  441. //--------------------------------------------------------------------------------------
  442. // Defines the sort order when CRenderable objects are sorted by Material.
  443. // The ordering in which renderables are sorted is:
  444. //      effect
  445. //      effect instance
  446. // Using this sort order causes all renderables of like effects to be grouped together
  447. //--------------------------------------------------------------------------------------
  448. inline bool greaterMaterial( CRenderable& lhs, CRenderable& rhs )
  449. {
  450.     // sort by effect, which can be costly to switch
  451.     if( rhs.getEffect() > lhs.getEffect() )
  452.         return true;
  453.     else if( rhs.getEffect() < lhs.getEffect() )
  454.         return false;
  455.  
  456.     // enforce the sorting by effect instance, lastly
  457.     // changing parameter values within an effect is likely to be less costly than switching effects.
  458.     return rhs.getEffectInstance() > lhs.getEffectInstance();
  459. }
  460.  
  461.  
  462. //--------------------------------------------------------------------------------------
  463. // Defines the sort order when CRenderable objects are sorted by Instance
  464. // The ordering in which renderables are sorted is:
  465. //      instance of mesh
  466. //      mesh subset
  467. // Using this sort order causes all object instances to be drawn in their entirety
  468. // before moving on to the next object instance to be drawn.
  469. //--------------------------------------------------------------------------------------
  470. inline bool greaterInstance( CRenderable& lhs, CRenderable& rhs )
  471. {
  472.     // sort by instance
  473.     if( rhs.getInstance() > lhs.getInstance() )
  474.         return true;
  475.     else if (rhs.getInstance() < lhs.getInstance() )
  476.         return false;
  477.     
  478.     // sort by mesh material subset
  479.     return rhs.getSubset() > lhs.getSubset();
  480. }
  481.  
  482. //--------------------------------------------------------------------------------------
  483. // Required for std::find to operate on D3DVERTEXELEMENT9
  484. //--------------------------------------------------------------------------------------
  485. inline bool operator==( const D3DVERTEXELEMENT9& lhs, const D3DVERTEXELEMENT9& rhs )
  486. {
  487.     return 0 == memcmp( &lhs, &rhs, sizeof( lhs ) );
  488. }
  489.